﻿using System;
using System.Drawing;
using System.IO;

namespace MJTop.NGif
{
    public class AnimatedGifEncoder
    {
        protected bool closeStream;

        protected int colorDepth;

        protected byte[] colorTab;

        protected int delay;

        protected int dispose = -1;

        protected bool firstFrame = true;

        protected Stream fs;

        protected int height;

        protected Image image;

        protected byte[] indexedPixels;

        protected int palSize = 7;

        protected byte[] pixels;

        protected int repeat = -1;

        protected int sample = 10;

        protected bool sizeSet;

        protected bool started;

        protected int transIndex;

        protected Color transparent = Color.Empty;

        protected bool[] usedEntry = new bool[256];

        protected int width;

        public AnimatedGifEncoder()
        {
        }

        public bool AddFrame(Image im)
        {
            bool flag;
            if ((im == null ? false : this.started))
            {
                bool flag1 = true;
                try
                {
                    if (!this.sizeSet)
                    {
                        this.SetSize(im.Width, im.Height);
                    }
                    this.image = im;
                    this.GetImagePixels();
                    try
                    {
                        this.AnalyzePixels();
                    }
                    catch (Exception exception)
                    {
                        flag = false;
                        return flag;
                    }
                    if (this.firstFrame)
                    {
                        this.WriteLSD();
                        this.WritePalette();
                        if (this.repeat >= 0)
                        {
                            this.WriteNetscapeExt();
                        }
                    }
                    this.WriteGraphicCtrlExt();
                    this.WriteImageDesc();
                    if (!this.firstFrame)
                    {
                        this.WritePalette();
                    }
                    this.WritePixels();
                    this.firstFrame = false;
                }
                catch (IOException oException)
                {
                    flag1 = false;
                }
                flag = flag1;
            }
            else
            {
                flag = false;
            }
            return flag;
        }

        protected void AnalyzePixels()
        {
            int length = (int)this.pixels.Length;
            int num = length / 3;
            this.indexedPixels = new byte[num];
            NeuQuant neuQuant = new NeuQuant(this.pixels, length, this.sample);
            this.colorTab = neuQuant.Process();
            int num1 = 0;
            for (int i = 0; i < num; i++)
            {
                int num2 = num1;
                num1 = num2 + 1;
                int num3 = num1;
                num1 = num3 + 1;
                byte[] numArray = this.pixels;
                int num4 = num1;
                num1 = num4 + 1;
                int num5 = neuQuant.Map(this.pixels[num2] & 255, this.pixels[num3] & 255, numArray[num4] & 255);
                this.usedEntry[num5] = true;
                this.indexedPixels[i] = (byte)num5;
            }
            this.pixels = null;
            this.colorDepth = 8;
            this.palSize = 7;
            if (this.transparent != Color.Empty)
            {
                this.transIndex = this.FindClosest(this.transparent);
            }
        }

        protected int FindClosest(Color c)
        {
            int num;
            if (this.colorTab != null)
            {
                int r = c.R;
                int g = c.G;
                int b = c.B;
                int num1 = 0;
                int num2 = 16777216;
                int length = (int)this.colorTab.Length;
                for (int i = 0; i < length; i++)
                {
                    int num3 = i;
                    i = num3 + 1;
                    int num4 = r - (this.colorTab[num3] & 255);
                    int num5 = i;
                    i = num5 + 1;
                    int num6 = g - (this.colorTab[num5] & 255);
                    int num7 = b - (this.colorTab[i] & 255);
                    int num8 = num4 * num4 + num6 * num6 + num7 * num7;
                    int num9 = i / 3;
                    if ((!this.usedEntry[num9] ? false : num8 < num2))
                    {
                        num2 = num8;
                        num1 = num9;
                    }
                }
                num = num1;
            }
            else
            {
                num = -1;
            }
            return num;
        }

        public bool Finish()
        {
            bool flag;
            if (this.started)
            {
                bool flag1 = true;
                this.started = false;
                try
                {
                    this.fs.WriteByte(59);
                    this.fs.Flush();
                    if (this.closeStream)
                    {
                        this.fs.Close();
                    }
                }
                catch (IOException oException)
                {
                    flag1 = false;
                }
                this.transIndex = 0;
                this.fs = null;
                this.image = null;
                this.pixels = null;
                this.indexedPixels = null;
                this.colorTab = null;
                this.closeStream = false;
                this.firstFrame = true;
                flag = flag1;
            }
            else
            {
                flag = false;
            }
            return flag;
        }

        protected void GetImagePixels()
        {
            int width = this.image.Width;
            int height = this.image.Height;
            if ((width != this.width ? true : height != this.height))
            {
                Image bitmap = new Bitmap(this.width, this.height);
                Graphics graphic = Graphics.FromImage(bitmap);
                graphic.DrawImage(this.image, 0, 0);
                this.image = bitmap;
                graphic.Dispose();
            }
            this.pixels = new byte[3 * this.image.Width * this.image.Height];
            int num = 0;
            Bitmap bitmap1 = new Bitmap(this.image);
            for (int i = 0; i < this.image.Height; i++)
            {
                for (int j = 0; j < this.image.Width; j++)
                {
                    Color pixel = bitmap1.GetPixel(j, i);
                    this.pixels[num] = pixel.R;
                    num++;
                    this.pixels[num] = pixel.G;
                    num++;
                    this.pixels[num] = pixel.B;
                    num++;
                }
            }
        }

        public void SetDelay(int ms)
        {
            this.delay = (int)Math.Round((double)((float)ms / 10f));
        }

        public void SetDispose(int code)
        {
            if (code >= 0)
            {
                this.dispose = code;
            }
        }

        public void SetFrameRate(float fps)
        {
            if (fps != 0f)
            {
                this.delay = (int)Math.Round((double)(100f / fps));
            }
        }

        public void SetQuality(int quality)
        {
            if (quality < 1)
            {
                quality = 1;
            }
            this.sample = quality;
        }

        public void SetRepeat(int iter)
        {
            if (iter >= 0)
            {
                this.repeat = iter;
            }
        }

        public void SetSize(int w, int h)
        {
            if ((!this.started ? true : this.firstFrame))
            {
                this.width = w;
                this.height = h;
                if (this.width < 1)
                {
                    this.width = 320;
                }
                if (this.height < 1)
                {
                    this.height = 240;
                }
                this.sizeSet = true;
            }
        }

        public void SetTransparent(Color c)
        {
            this.transparent = c;
        }

        public bool Start(Stream os)
        {
            bool flag;
            if (os != null)
            {
                bool flag1 = true;
                this.closeStream = false;
                this.fs = os;
                try
                {
                    this.WriteString("GIF89a");
                }
                catch (IOException oException)
                {
                    flag1 = false;
                }
                bool flag2 = flag1;
                bool flag3 = flag2;
                this.started = flag2;
                flag = flag3;
            }
            else
            {
                flag = false;
            }
            return flag;
        }

        public bool Start(string file)
        {
            bool flag = true;
            try
            {
                this.fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
                flag = this.Start(this.fs);
                this.closeStream = true;
            }
            catch (IOException oException)
            {
                flag = false;
            }
            bool flag1 = flag;
            bool flag2 = flag1;
            this.started = flag1;
            return flag2;
        }

        protected void WriteGraphicCtrlExt()
        {
            int num;
            int num1;
            this.fs.WriteByte(33);
            this.fs.WriteByte(249);
            this.fs.WriteByte(4);
            if (this.transparent != Color.Empty)
            {
                num = 1;
                num1 = 2;
            }
            else
            {
                num = 0;
                num1 = 0;
            }
            if (this.dispose >= 0)
            {
                num1 = this.dispose & 7;
            }
            num1 <<= 2;
            this.fs.WriteByte(Convert.ToByte(num1 | num));
            this.WriteShort(this.delay);
            this.fs.WriteByte(Convert.ToByte(this.transIndex));
            this.fs.WriteByte(0);
        }

        protected void WriteImageDesc()
        {
            this.fs.WriteByte(44);
            this.WriteShort(0);
            this.WriteShort(0);
            this.WriteShort(this.width);
            this.WriteShort(this.height);
            if (!this.firstFrame)
            {
                this.fs.WriteByte(Convert.ToByte(128 | this.palSize));
            }
            else
            {
                this.fs.WriteByte(0);
            }
        }

        protected void WriteLSD()
        {
            this.WriteShort(this.width);
            this.WriteShort(this.height);
            this.fs.WriteByte(Convert.ToByte(240 | this.palSize));
            this.fs.WriteByte(0);
            this.fs.WriteByte(0);
        }

        protected void WriteNetscapeExt()
        {
            this.fs.WriteByte(33);
            this.fs.WriteByte(255);
            this.fs.WriteByte(11);
            this.WriteString("NETSCAPE2.0");
            this.fs.WriteByte(3);
            this.fs.WriteByte(1);
            this.WriteShort(this.repeat);
            this.fs.WriteByte(0);
        }

        protected void WritePalette()
        {
            this.fs.Write(this.colorTab, 0, (int)this.colorTab.Length);
            int length = 768 - (int)this.colorTab.Length;
            for (int i = 0; i < length; i++)
            {
                this.fs.WriteByte(0);
            }
        }

        protected void WritePixels()
        {
            (new LZWEncoder(this.width, this.height, this.indexedPixels, this.colorDepth)).Encode(this.fs);
        }

        protected void WriteShort(int value)
        {
            this.fs.WriteByte(Convert.ToByte(value & 255));
            this.fs.WriteByte(Convert.ToByte(value >> 8 & 255));
        }

        protected void WriteString(string s)
        {
            char[] charArray = s.ToCharArray();
            for (int i = 0; i < (int)charArray.Length; i++)
            {
                this.fs.WriteByte((byte)charArray[i]);
            }
        }
    }
}